#include <QtCore/QObject>
#include <QtGui/QKeyEvent>
#include <QyDebug.h>
#include "QyGLInputController.h"
#include "QyGLScene.h"

using namespace QyGL;

/**************************************************************************************************/

class QyWidgetEventFilter : public QObject {
public:
    explicit QyWidgetEventFilter( Scene * target, InputController * controller )
        : QObject(target)
    {

        p_scene = target;
        p_controller = controller;

    }

    bool eventFilter( QObject * object, QEvent * event ) {


        if ( p_controller && p_controller->isEnabled() ) {

            int eventType = event->type();

            switch (eventType) {

            case QEvent::GraphicsSceneMousePress:

                if ( QGraphicsSceneMouseEvent * mouseEvent = dynamic_cast<QGraphicsSceneMouseEvent*>(event) ) {

                    MouseEvent myMouseEvent( mouseEvent, p_scene ? p_scene->cursorPos() : QPoint(),
                        p_scene ? p_scene->glCursor() : QyMath::Vector() );

//                     QyDbgValue( myMouseEvent.glPos().toPointF() );

                    p_controller->p_pressPos = myMouseEvent.pos();
                    p_controller->p_pressOffset = p_controller->p_pressPos;
                    p_controller->p_moveOffset = p_controller->p_pressPos;
                    p_controller->p_glPressPos = myMouseEvent.glPos();
                    p_controller->p_glPressOffset = p_controller->p_glPressPos;
                    p_controller->p_glMoveOffset = p_controller->p_glPressPos;
                    p_controller->p_pressButtons = myMouseEvent.buttons();
                    p_controller->p_mousePressed = true;
                    p_controller->mousePressd(myMouseEvent);
                    p_controller->p_lastMovePos = p_controller->p_pressPos;
                    p_controller->p_glLastMovePos = p_controller->p_glPressPos;

                } else {

                    qWarning( "Detected invalid mouse press event object." );

                }

                break;

            case QEvent::GraphicsSceneMouseMove:

                if ( QGraphicsSceneMouseEvent * mouseEvent = dynamic_cast<QGraphicsSceneMouseEvent*>(event) ) {

                    MouseEvent myMouseEvent( mouseEvent, p_scene ? p_scene->cursorPos() : QPoint(),
                        p_scene ? p_scene->glCursor() : QyMath::Vector() );

//                     QyDbgValue( myMouseEvent.glPos().toPointF() );

                    p_controller->p_pressOffset = p_controller->p_pressPos - myMouseEvent.pos();
                    p_controller->p_moveOffset = p_controller->p_lastMovePos - myMouseEvent.pos();
                    p_controller->p_glPressOffset = p_controller->p_glPressPos - myMouseEvent.glPos();
                    p_controller->p_glMoveOffset = p_controller->p_glLastMovePos - myMouseEvent.glPos();
                    p_controller->mouseMoved(myMouseEvent);
                    p_controller->p_lastMovePos = myMouseEvent.pos();
                    p_controller->p_glLastMovePos = myMouseEvent.glPos();

                } else {

                    qWarning( "Detected invalid mouse move event object." );

                }

                break;

            case QEvent::GraphicsSceneMouseRelease:

                if ( QGraphicsSceneMouseEvent * mouseEvent = dynamic_cast<QGraphicsSceneMouseEvent*>(event) ) {

                    MouseEvent myMouseEvent( mouseEvent, p_scene ? p_scene->cursorPos() : QPoint(),
                        p_scene ? p_scene->glCursor() : QyMath::Vector() );

                    p_controller->p_pressButtons = mouseEvent->buttons();
                    p_controller->p_mousePressed = ( p_controller->p_pressButtons != Qt::NoButton );
                    p_controller->mouseReleased(myMouseEvent);

                } else {

                    qWarning( "Detected invalid mouse release event object." );

                }
                break;

            case QEvent::KeyPress:

                if ( QKeyEvent * keyEvent = dynamic_cast<QKeyEvent*>(event) ) {

                    p_controller->keyPressd(keyEvent);

                } else {

                    qWarning( "Detected invalid key event object." );

                }

                break;

            case QEvent::KeyRelease:

                if ( QKeyEvent * keyEvent = dynamic_cast<QKeyEvent*>(event) ) {

                    p_controller->keyReleased(keyEvent);

                } else {

                    qWarning( "Detected invalid key event object." );

                }

                break;

            }

        }

        return QObject::eventFilter( object, event );
    }

    Scene * p_scene;
    InputController * p_controller;

};

/**************************************************************************************************/

MouseEvent::MouseEvent( QGraphicsSceneMouseEvent * event, const QPoint & point, const QyMath::Vector & glCursor )
    : QMouseEvent( QEvent::User, point, point, event->button(),
    event->buttons(), event->modifiers() )
{

    p_glCursor = glCursor;

}


/**************************************************************************************************/

InputController::InputController( Scene * target ) {

    p_widgetEventFilter = new QyWidgetEventFilter( target, this );
    p_enabled = true;
    p_mousePressed = false;
    p_pressButtons = Qt::NoButton;

    if (p_widgetEventFilter->p_scene)
        p_widgetEventFilter->p_scene->installEventFilter(p_widgetEventFilter);

}

InputController::~InputController() {

    delete p_widgetEventFilter;

}

Scene * InputController::scene() const {

    return p_widgetEventFilter->p_scene;
}

bool InputController::match( const QGraphicsSceneMouseEvent * event, int buttons, int modifiers ) {

    if ( modifiers >= 0 && modifiers != event->modifiers() )
        return false;

    return buttons == event->buttons();
}

bool InputController::match( const QKeyEvent * event, int key, int modifiers ) {

    if ( modifiers >= 0 && modifiers != event->modifiers() )
        return false;

    return key == event->key();
}

/**************************************************************************************************/

int BasicCamViewController::mode( int buttons ) const {

    QMapIterator<int,int> i(p_modeButtons);

    while ( i.hasNext() ) {

        i.next();

        if ( i.value() == buttons )
            return i.key();

    }

    return None;
}

void BasicCamViewController::mousePressd( const MouseEvent & event ) {

    Q_UNUSED(event);

    p_pressCamPos = scene()->camPos();

}

void BasicCamViewController::mouseMoved( const MouseEvent & event ) {

    Scene * scene = BasicCamViewController::scene();

    if ( scene && isMousePressed() ) switch ( mode( pressButtons() ) ) {

    case Move:
        {

            QyMath::Vector camPos = scene->camPos();

            camPos.move( moveOffset().toPoint().manhattanLength() / 10.0, -QyMath::zDirection(
                QPointF( event.pos() ), QPointF( lastMovePos() ) ) + scene->camDir().p[2] );
            scene->setCamPos(camPos);
            scene->update();

        }
        break;

    case Rotate:
        {

            QyMath::Vector camDir = scene->camDir();

            camDir.p[0] -= moveOffset().y();
            camDir.p[2] -= moveOffset().x();

            scene->setCamDir(camDir);

        }
        break;

    case Zoom:
        {

            qreal zoomFixMin = minZoom,
                zoomFixMax = -maxZoom;

            if ( zoomFixMin < zoomFixMax )
                qSwap( zoomFixMin, zoomFixMax );

            QyMath::Vector camOffset = scene->camOffset();

            camOffset.p[2] -= moveOffset().y();

            if ( camOffset.p[2] > zoomFixMin )
                camOffset.p[2] = zoomFixMin;

            if ( camOffset.p[2] < zoomFixMax )
                camOffset.p[2] = zoomFixMax;

            scene->setCamOffset(camOffset);

        }
        break;

    }

}

/**************************************************************************************************/

void BaseKeyController::keyPressd( QKeyEvent * event ) {

    // qDebug() << "Qt::Key_C" << Qt::Key_C;
    // qDebug() << "event->key()" << event->key();

    int key = event->key();

    if ( !p_keys.contains(key) )
        p_keys.append(key);

}

void BaseKeyController::keyReleased( QKeyEvent * event ) {

    p_keys.removeAll( event->key() );

}
